﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using PacMan.Components;

namespace MiniPac.Components
{
	public enum GType
	{
		B,	//Blinky
		I,	//Inky
		P,	//Pinky
		C		//Clyde...
	}
	class Ghost : Player
	{
		protected Player player;
		protected GType type;
		private List<Path> exits = new List<Path>(4);
		private double gVel;
		Random rnd = new Random();
		protected bool IsDead = false;
		protected Point reset;

		public Ghost(Player p, GType type, Maze m, double x, double y, Color c)
			: base(m, x, y, c)
		{
			player = p;
			this.type = type;
			currDir = nextDir = Dir.Still;
			InitVel();
		}

		public Ghost(Player p, GType type, Maze m, int x, int y, Color c)
			: base(m, x, y, c)
		{
			player = p;
			this.type = type;
			currDir = nextDir = Dir.Still;
			InitVel();
		}

		public Ghost(Player p, GType type, Maze m)
			: base(m, 0, 0, Color.Gray)
		{
			currDir = Dir.Left;
			nextDir = Dir.Down;
			reset = new Point(maze.MazeTileSizeX / 2, 11);

			switch (type)
			{
				case GType.B:						//Blinky
					color = Color.Red;
					SetCurrTile(14, 11);
					CenterX();
					player = p;
					this.type = type;
					maze = m;
					currDir = Dir.Left;
					break;
				case GType.I:					//Inky
					color = Color.BlueViolet;
					SetCurrTile(14, 14);
					CenterX();
					player = p;
					this.type = type;
					maze = m;
					break;
				case GType.P:					//Pinky
					color = Color.PeachPuff;
					SetCurrTile(14, 14);
					CenterX();
					player = p;
					this.type = type;
					maze = m;
					break;
				case GType.C:					//Clyde
					color = Color.Orange;
					SetCurrTile(14, 14);
					CenterX();
					player = p;
					this.type = type;
					maze = m;
					break;
			}
			InitVel();
		}

		private Point GetPlayerTile()
		{
			return player.GetCurrentTile();
		}

		public override void Draw(SpriteBatch sb)
		{
			if (IsDead)
			{
				sb.Draw(R.Ghost, BB, R.deadCol);
			}
			else if (State.scaredGhosts)
			{
				sb.Draw(R.Ghost, BB, R.blueCol);
			}
			else
			{
				sb.Draw(R.Ghost, BB, color);
			}
#if DEBUG__
			Color c0;
			Color c1 = new Color(255, 0, 10, 180);
			Color c2 = new Color(0, 255, 100, 89);

			Point cp = player.GetCurrentTile();
			Point cg = GetCurrentTile();

			for (int i = 0; i < exits.Count; i++)
			{
				if (i == 0) {c0 = c1;}
				else {c0 = c2; }
					sb.Draw(R.Ghost, new Rectangle(exits[i].start.X*s + R.MarginX, exits[i].start.Y*s + R.MarginY, s, s), c0);
				  sb.DrawString(R.c64_font, "len:" + exits[i].lenght, new Vector2(R.StageSizeX, (130 + (i+1)*15)), c0);
				
			}
			sb.Draw(R.Ghost, new Rectangle(GetPlayerTile().X * s + R.MarginX, GetPlayerTile().Y * s + R.MarginY, s, s), c2);
			//   sb.DrawString(R.c64_font, "Points:" + points.ToString(), new Vector2(R.StageSizeX, 130), Color.White);

			sb.DrawString(R.c64_font, "G: " + cg.X + ","+ cg.Y, new Vector2(R.StageSizeX, 175), Color.Red);
			sb.DrawString(R.c64_font, "P: " + cp.X + "," + cp.Y, new Vector2(R.StageSizeX, 190), Color.Yellow);


#endif
		}

		public override void Update(double time)
		{
			//sesurrects ghost
			if (GetCurrentTile().Equals(reset))
			{
				IsDead = false;
			}

			if (GetCurrentTile().Equals(GetPlayerTile()))
			{
				if (!State.scaredGhosts)
				{

					if (R.snd_die_inst.State == SoundState.Stopped)
					{
						R.snd_die_inst.Play();
						State.lives--;
						player.ResetPos();
					}
				}
				else
				{
					if (!IsDead)
					{
						IsDead = true;
						R.snd_eat_ghost.Play();
						State.pnts += 30;
					}
				}
			}

			if (!((GetCurrentTile().Y == 14) && (GetCurrentTile().X < 2)) ||
					!((GetCurrentTile().Y == 14) && (GetCurrentTile().X > R.MazeXSize - 3)))
			{
				if (StepsOverCentre(currDir)) // We are going over border...
				{
					if (IsWall(currDir)) //  if we hit a wall
					{
						AllignToTile(); //  Align to the tile
						currDir = nextDir; //  Change direction 
						nextDir = GetNextDir(); //GetRandomDir();						//  and compute a new direction
					}
					if (!IsWall(nextDir))
					{
						currDir = nextDir;
						nextDir = GetNextDir();
					}
				}
			}
			UpdatePos(currDir, gVel);

			#region Special case for "the teleport"...

			int teleY = GetCurrentTile().Y;

			if (teleY == 14 && ScreenX <= R.MarginX + R.Size && currDir == Dir.Left)
			{
				SetCurrTile(R.MazeXSize - 1, 14);
			}
			else if (teleY == 14 && ScreenX >= maze.MazePixelSizeX - R.Size && currDir == Dir.Right)
			{
				SetCurrTile(0, 14);
			}

			#endregion

		}

		private Dir GetNextDir()
		{
			UpdateExits(currDir);
			exits.Sort();
			return exits[0].dir;
		}

		private void UpdateExits(Dir dir)
		{
			exits.Clear();

			Point pacman = GetTarget();

			Point nextTile = GetNextTile(dir);
			Point up = new Point(nextTile.X, nextTile.Y - 1);
			Point right = new Point(nextTile.X + 1, nextTile.Y);
			Point down = new Point(nextTile.X, nextTile.Y + 1);
			Point left = new Point(nextTile.X - 1, nextTile.Y);

			switch (dir)
			{
				case Dir.Up:
					if (!IsWall(up))
					{
						exits.Add(new Path(GetDistance(up, pacman), up, Dir.Up));
					}
					if (!IsWall(right))
					{
						exits.Add(new Path(GetDistance(right, pacman), right, Dir.Right)); ;
					}
					if (!IsWall(left))
					{
						exits.Add(new Path(GetDistance(left, pacman), left, Dir.Left)); ;
					}
					break;
				case Dir.Right:
					if (!IsWall(up))
					{
						exits.Add(new Path(GetDistance(up, pacman), up, Dir.Up));
					}
					if (!IsWall(right))
					{
						exits.Add(new Path(GetDistance(right, pacman), right, Dir.Right)); ;
					}
					if (!IsWall(down))
					{
						exits.Add(new Path(GetDistance(down, pacman), down, Dir.Down)); ;
					}
					break;
				case Dir.Down:
					if (!IsWall(right))
					{
						exits.Add(new Path(GetDistance(right, pacman), right, Dir.Right)); ;
					}
					if (!IsWall(down))
					{
						exits.Add(new Path(GetDistance(down, pacman), down, Dir.Down)); ;
					}
					if (!IsWall(left))
					{
						exits.Add(new Path(GetDistance(left, pacman), left, Dir.Left)); ;
					}
					break;
				case Dir.Left:
					if (!IsWall(up))
					{
						exits.Add(new Path(GetDistance(up, pacman), up, Dir.Up));
					}
					if (!IsWall(down))
					{
						exits.Add(new Path(GetDistance(down, pacman), down, Dir.Down)); ;
					}
					if (!IsWall(left))
					{
						exits.Add(new Path(GetDistance(left, pacman), left, Dir.Left)); ;
					}
					break;
			}
		}

		//Is the ghost stepping over to the next tile?
		protected bool StepsOverTile(Dir dir)
		{
			Tile currTile = GetTile(GetCurrentTile());
			bool res;

			switch (dir)
			{
				case Dir.Up:
					AbstractY -= gVel;
					res = BB.Center.Y < currTile.BB.Top;
					AbstractY += gVel;
					return res;
				case Dir.Right:
					AbstractX += gVel;
					res = BB.Center.X > currTile.BB.Right;
					AbstractX -= gVel;
					return res;
				case Dir.Down:
					AbstractY += gVel;
					res = BB.Center.Y > currTile.BB.Bottom;
					AbstractY -= gVel;
					return res;
				case Dir.Left:
					AbstractX -= gVel;
					res = BB.Center.X < currTile.BB.Left;
					AbstractX += gVel;
					return res;
				default:
					return false;
			}
		}

		//Distance between tiles
		protected int GetDistance(Point s, Point d)
		{
			return Math.Abs(s.X - d.X) + Math.Abs(s.Y - d.Y);
		}

		private void InitVel()
		{
			switch (type)
			{
				case GType.B:
					gVel = R.bliVel;
					break;
				case GType.I:
					gVel = R.inkVel;
					break;
				case GType.P:
					gVel = R.pinVel;
					break;
				case GType.C:
					gVel = R.clyVel;
					break;
			}
		}

		private Dir GetRandomDir()
		{
			int rand = rnd.Next(0, 3);

			switch (rand)
			{
				case 0:
					return Dir.Up;
				case 1:
					return Dir.Right;
				case 2:
					return Dir.Down;
				case 3:
					return Dir.Left;
				default:
					return Dir.Still;
			}
		}

		private Point GetTarget()
		{
			if (IsDead)
			{
				return new Point(maze.MazeTileSizeX / 2, 11);
			}
			if (InNest())
			{
				return new Point(maze.MazeTileSizeX / 2, 11);
			}
			Point pacman = GetPlayerTile();
			switch (type)
			{
				case GType.B:
					return pacman;
				case GType.I:
					if (player.Dir == Dir.Up)
					{
						pacman.X = pacman.X - 4;
						pacman.Y = pacman.Y + 4;
					}
					if (player.Dir == Dir.Right)
					{
						pacman.X = pacman.X + 4;
					}
					if (player.Dir == Dir.Down)
					{
						pacman.Y = pacman.Y + 4;
					}
					if (player.Dir == Dir.Left)
					{
						pacman.X = pacman.X - 4;
					}
					return pacman;
				case GType.P:
					if (player.Dir == Dir.Up)
					{
						pacman.Y = pacman.Y + 4;
					}
					if (player.Dir == Dir.Right)
					{
						pacman.X = pacman.X + 4;
						pacman.Y = pacman.Y - 4;
					}
					if (player.Dir == Dir.Down)
					{
						pacman.Y = pacman.Y + 4;
					}
					if (player.Dir == Dir.Left)
					{
						pacman.X = pacman.X - 4;
					}
					return pacman;
				case GType.C:
					if (GetDistance(GetCurrentTile(), pacman) < 8)
					{
						return pacman;
					}
					return new Point(0, maze.MazeTileSizeY);
			}


			return new Point();
		}

		private bool InNest()
		{
			Point cTile = GetCurrentTile();
			return ((cTile.X >= 11 && cTile.Y >= 13) && (cTile.X <= 16 && cTile.Y <= 15)) ;
		}

	}
	/* switch dir...
	 switch (dir)
			{
				case Dir.Up:
					break;
				case Dir.Right:
					break;
				case Dir.Down:
					break;
				case Dir.Left:
					break;
				default:
					break;
			}
	 */
}
